package springframework.beans.factory.support;

import cn.hutool.core.bean.BeanUtil;
import springframework.beans.BeansException;
import springframework.beans.PropertyValue;
import springframework.beans.PropertyValues;
import springframework.beans.factory.config.BeanDefinition;
import springframework.beans.factory.config.BeanReference;

import java.lang.reflect.Constructor;

/**
 * @author lzp
 * @Date:2023/3/10
 * @description: 抽象自动注入bean工厂
 */
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

	/**
	 * 暂时直接走cglib的策略，后续可调整由用户自行决定
	 */
	private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

	@Override
	public Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException {
		// 通过class使用反射创建对象
		Object bean = null;
		try {
			bean = beanDefinition.getBeanClass().newInstance();
			// 给bean填充属性
			applyPropertyValues(beanName, bean, beanDefinition);
		} catch (InstantiationException | IllegalAccessException e) {
			throw new BeansException("Instantiation of bean failed", e);
		}
		// 添加进单例对象缓存中
		addSingleton(beanName, bean);
		return bean;
	}

	/**
	 * 多参数构造的情况创建bean
	 *
	 * @param beanName       bean的名称
	 * @param beanDefinition bean的包装对象
	 * @param args           多参构造函数的入参
	 */
	protected Object createBeanInstance(String beanName, BeanDefinition beanDefinition, Object[] args) {
		Constructor constructorToUser = null;
		Class<?> beanClass = beanDefinition.getBeanClass();
		Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
		for (Constructor ctor : declaredConstructors) {
			if (null != args && ctor.getParameterTypes().length == args.length) {
				constructorToUser = ctor;
				break;
			}
		}
		// 走实际的策略实现类进行对象的实例化
		return instantiationStrategy.instantiate(beanDefinition, beanName, constructorToUser, args);
	}

	/**
	 * bean的属性填充
	 *
	 * @param beanName       bean名称
	 * @param bean           bean对象
	 * @param beanDefinition bean的包装对象
	 */
	protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
		try {
			PropertyValues propertyValues = beanDefinition.getPropertyValues();
			for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
				String name = propertyValue.getName();
				Object value = propertyValue.getValue();
				if (value instanceof BeanReference) {
					// A依赖B 获取B的实例
					BeanReference beanReference = (BeanReference) value;
					value = getBean(beanReference.getBeanName());
				}
				// 属性填充
				BeanUtil.setFieldValue(bean, name, value);
			}
		} catch (Exception e) {
			throw new BeansException("Error setting property values：" + beanName);
		}
	}

	public InstantiationStrategy getInstantiationStrategy() {
		return instantiationStrategy;
	}

	public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
		this.instantiationStrategy = instantiationStrategy;
	}
}
